#include "ex06fa.cpp"
#include <stdint.h>

// [[ex06fb]]
// DEBUG descr The queue handler of the function foo().
class FooQueueThread : public QueueThread
{
public:
	enum FooMessageTypes {
		MSG_REQUEST,
		MSG_CALL_BAR
	};
	// the request that will be sent to foo
	class FooRequestMessage : public QueueMessage
	{
	public:
		QueueThread *requestor_; // where to send the reply

		FooRequestMessage(int rettype, 
				QueueThread *requestor, void *context) :
			QueueMessage(MSG_REQUEST, rettype, context),
			requestor_(requestor)
		{ }
	};
	// results returned from foo, just a notification
	// that it's done
	class FooReplyMessage : public QueueMessage
	{
	public:
		FooReplyMessage(int rettype, void *context) :
			QueueMessage(rettype, -1, context)
		{ }
	};

protected:
	struct FooContext {
		FooRequestMessage *request_;
		int a_;
		int b_;
	};
	void dispatch(QueueMessage *m) 
	{
		switch (m->getType()) {
		case MSG_REQUEST:
			processRequest((FooRequestMessage *)m);
			break;
		case MSG_CALL_BAR:
			processCallBar(
				(BarQueueThread::BarReplyMessage *)m);
			break;
		default:
			delete m;
			break;
		}
	}
	void processRequest(FooRequestMessage *m) 
	{
		FooContext *ctx = new FooContext;
		ctx->request_ = m;
	// DEBUG {
		ctx->b_ = 10;
	// DEBUG }
		// ... calculate ctx->b_
		bar->post(Queue::PRIORITY_NORMAL, 
			new BarQueueThread::BarRequestMessage(
				MSG_CALL_BAR, ctx, this, ctx->b_));
	}
	void processCallBar(
		BarQueueThread::BarReplyMessage *m) 
	{
		FooContext *ctx = (FooContext *)m->getContext();

		ctx->a_ = m->result_;
		delete m;
		// ...
	// DEBUG {
		fprintf(stderr, "foo received result %d from bar\n", ctx->a_);
	// DEBUG }
		FooRequestMessage *rq = ctx->request_;
		rq->requestor_->post(Queue::PRIORITY_HIGH, new 
			FooReplyMessage(rq->getRetType(), 
			rq->getContext()));
		delete rq;
		delete ctx;
	}

};

// the way to find the foo thread, will be
// initialized from main()
static FooQueueThread *foo;
// END

// DEBUG {

class DummyQueueThread : public QueueThread
{
public:
	virtual void dispatch(QueueMessage *m)
	{
		delete m;
	}
};

int main()
{
	Queue barQueue;
	bar = new BarQueueThread;
	bar->setQueue(&barQueue);

	Queue fooQueue;
	foo = new FooQueueThread;
	foo->setQueue(&fooQueue);

	bar->start();
	foo->start();

	// ------------

	Queue mainQueue;
	DummyQueueThread mainThread;
	mainThread.setQueue(&mainQueue);

	foo->post(Queue::PRIORITY_NORMAL, new FooQueueThread::FooRequestMessage(0, &mainThread, (void *)1));
	foo->post(Queue::PRIORITY_NORMAL, new FooQueueThread::FooRequestMessage(0, &mainThread, (void *)2));

	FooQueueThread::FooReplyMessage *reply;
	for (int i = 0; i < 2; i++) {
		reply = (FooQueueThread::FooReplyMessage*)mainQueue.pop_front();
		fprintf(stderr, "foo returned reply %p\n", reply->getContext());
		delete reply;
	}
	
	// ------------
	
	bar->post(Queue::PRIORITY_NORMAL, new Message(Message::EXIT));
	foo->post(Queue::PRIORITY_NORMAL, new Message(Message::EXIT));

	bar->join();
	foo->join();

	delete bar;
	delete foo;
	return 0;
}
// DEBUG }
/* Sample output:
foo received result 10 from bar
foo received result 20 from bar
foo returned reply 0x1
foo returned reply 0x2
*/
